const fs = require('fs-extra');
const path = require('path');
const utility = require('../../../utility');

const ParentReporter = require('./../default-report');
const ProjectVersionSpliter = require('./../../project-version-spliter');
const files = require('./../../../files');
const { mcdcKind } = require('./../../../tests');
const invokeRelations = require('./../../../invoke-relations');
const {
  generateStatemetBlocks,
  generateBranchBlocks,
  generateConditionBlocks,
  generateMcDcBlocks,
  generateCondMcDcBlocks,
  generateDecisionBlocks,
  generateInvokeImage,
} = require('./../functionUtil');

class DefaultReporter extends ParentReporter {
  constructor(...rest) {
    super(...rest);
    this.baseDir = __dirname;
  }

  async handleFile(file, versionDir) {
    const reportPath = `${path.join(versionDir, path.dirname(file.path), file.fileName)}`;
    const res = await ProjectVersionSpliter.prototype.handleFile.call(this, file, versionDir);
    const { typeSystem, functions: funcs, resultCount } = res;
    const imagesDic = [];
    if (!resultCount) {
      return Promise.resolve();
    }
    if (this.isIntegrationTest()) {
      const relations = await Promise.all(funcs.map((func) => invokeRelations.retrieveRelations(func._id, {
        initRelations: false,
        completeDepth: true,
      })));
      funcs.forEach((func, index) => {
        func.relations = relations[index];
      });
      await Promise.all(funcs.map(async (func) => {
        const imgPth = await generateInvokeImage(func, func.relations, 3, undefined, this.outputDir);
        func.invokeRelationsImage = imgPth;
      }));
      await utility.arrayChunkOperation(funcs, 1,
        async (func) => Promise.all(func.tests.map(async (testData) => {
          const imgPth = await generateInvokeImage(func, func.relations, 3, testData, this.outputDir);
          testData.testInvokeRelationsImage = imgPth;
        })));
    }
    const fileContent = await files.getLinesOfRemoteFileStream(file._id, {});
    const linesInSourceFile = fileContent.map(({ content }) => content);
    await utility.arrayChunkOperation(funcs, 10, async (func) => {
      func.functionCfg = await this.getDataAdapter().getFunctionCfg(func);
    });
    if (this.willTestStatements()) {
      generateStatemetBlocks(funcs, linesInSourceFile);
    }
    if (this.willTestBranch()) {
      generateBranchBlocks(funcs, linesInSourceFile);
    }
    if (this.willTestCondition()) {
      generateConditionBlocks(funcs);
    }
    if (this.willTestDecision()) {
      generateDecisionBlocks(funcs, linesInSourceFile);
    }
    if (this.willTestMcdc()) {
      // 算法一
      if (this.version.mcdcKind === mcdcKind.masking) {
        generateMcDcBlocks(funcs);
      // 算法二
      } else {
        generateCondMcDcBlocks(funcs);
      }
    }
    funcs.map((func) => {
      func.codeCoverageDetails = [];
      if (this.willTestStatements()) {
        func.codeCoverageDetails.push(func.statementsBlocks);
      }
      if (this.willTestBranch() && func.branchTotal > 0) {
        func.codeCoverageDetails.push(func.branchBlocks);
      }
      if (this.willTestCondition() && func.conditionTotal > 0) {
        func.codeCoverageDetails.push(func.conditionsBlocks);
      }
      if (this.willTestDecision() && func.decisionTotal > 0) {
        func.codeCoverageDetails.push(func.decisionBlocks);
      }
      if (this.willTestMcdc() && func.mcdcTotal > 0) {
        func.codeCoverageDetails.push(func.mcdcBlocks || func.condMcdcBlocks);
      }
      return func;
    });
    await fs.ensureDir(path.dirname(reportPath));
    const summary = DefaultReporter.getSummaryOfFile(funcs);
    const functions = this.getFunctionDetailsOfFile(typeSystem, funcs);
    if (this.isIntegrationTest()) {
      functions.forEach((func) => {
        imagesDic.push({
          target: func,
          key: 'invokeRelationsImage',
          value: func.invokeRelationsImage,
        });
        func.tests.forEach((test) => {
          imagesDic.push({
            target: test,
            key: 'testInvokeRelationsImage',
            value: test.testInvokeRelationsImage,
          });
        });
      });
    }
    await this.generateFileReport(
      {
        fileName: file.fileName,
        summary,
        functions,
        isIntegrationTest: this.isIntegrationTest(),
        reportName: this.getReportName(),
        headerRightContent: `${this.getReportName()} - ${file.fileName}`,
      },
      reportPath,
      imagesDic,
    );
    return { reportPath };
  }

  generateFileReport(data, output, imagesDic) {
    data.jumpable = this.jumpable;
    return this.generateReport('file-template', data, output, { copyStatic: true, imagesDic });
  }

  getFunctionDetailsOfFile(typeSystem, functions) {
    const resFunctions = [];
    functions.sort((a, b) => {
      if (a.index < b.index) return -1;
      if (a.index > b.index) return 1;
      return 0;
    });
    functions.forEach((func) => {
      if (!func.tests.length) {
        return;
      }
      resFunctions.push({
        functionName: func.functionName,
        description: func.description,
        udfs: func.udfs,
        codeDefects: this.getCodeDefects(func),
        // to be modified
        summary: this.getSummaryOfFunction(func),
        tests: this.getTestDetailsOfFunction(typeSystem, func),
        integrationFunctions: func.integrationFunctions,
        integrationFunctionsCount: func.integrationFunctions.length,
        invokeRelationsImage: func.invokeRelationsImage,
      });
    });
    return resFunctions;
  }

  getTestDetailsOfFunction(typeSystem, func) {
    const arr = super.getTestDetailsOfFunction(typeSystem, func);
    arr.forEach((tc) => {
      const originalTest = func.tests.find((item) => item._id.toString() === tc.testId.toString());
      if (originalTest) {
        tc.testInvokeRelationsImage = originalTest.testInvokeRelationsImage;
      }
    });
    return arr;
  }
}

/**
 * Export model definition object.
 */
module.exports = DefaultReporter;
